001 /* 002 * Copyright 2004-2006 Stephen J. McConnell. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 013 * implied. 014 * 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018 019 package net.dpml.metro.data; 020 021 import java.io.IOException; 022 import java.net.URI; 023 import java.util.Arrays; 024 025 import net.dpml.component.ActivationPolicy; 026 import net.dpml.component.Directive; 027 028 import net.dpml.metro.info.PartReference; 029 import net.dpml.metro.info.LifestylePolicy; 030 import net.dpml.metro.info.CollectionPolicy; 031 032 import net.dpml.lang.Part; 033 import net.dpml.lang.AbstractDirective; 034 035 /** 036 * Definition of the criteria for an explicit component profile. A profile, when 037 * included within the scope of a container declaration will be instantiated in 038 * the model as an EXPLICIT component profile resulting in the initiation of 039 * dependency resolution relative to the component as the target deployment 040 * objective. Multiple supplementary profiles may be packaged in a .xprofiles 041 * resources and will be assigned to the container automatically. In the absence 042 * of explicit or packaged profile directives, an implicit profile will be created 043 * for any component types declared under a jar manifest. 044 * 045 * <p><b>XML</b></p> 046 * <p>A component element declares the profile to be applied during the instantiation 047 * of a component type. It includes a name and class declaration, logging directives 048 * (resolved relative to the component's container), context creation criteria, 049 * together with configuration or parameters information.</p> 050 * 051 * <pre> 052 <font color="gray"><i><!-- 053 Declaration of the services hosted by this container. Service container here 054 will be managed relative to other provider components at the same level and 055 may be serviced by components declared in parent container. 056 --></i></font> 057 058 <component name="<font color="darkred">complex</font>" class="<font color="darkred">org.apache.avalon.playground.ComplexComponent</font>" activation="<font color="darkred">startup</font>"> 059 060 <font color="gray"><i><!-- 061 Priority and target assignments for component specific logging categrories. 062 --></i></font> 063 064 <categories priority="<font color="darkred">DEBUG</font>"> 065 <category name="<font color="darkred">init</font>" priority="<font color="darkred">DEBUG</font>" /> 066 </categories> 067 068 <font color="gray"><i><!-- 069 Context entry directives are normally only required in the case where the component 070 type declares a required context type and entry values. Generally speaking, a component 071 will normally qualify it's instantiation criteria through a configuration declaration. 072 Any context values defined at this level will override context values supplied by the 073 container. The following two context directives for "location" and "home" demonstrate 074 programatics creation of context values. The first entry declares that the context 075 value to be assigned to the key "location" shall be the String value "Paris". The second 076 context enty assignes the container's context value for "urn:avalon:home" to the component's 077 context key of "home". 078 --></i></font> 079 080 <context> 081 <entry key="<font color="darkred">location</font>"><font color="darkred">Paris</font></entry> 082 <include name="<font color="darkred">urn:avalon:home</font>" key="<font color="darkred">home</font>"/> 083 </context> 084 085 </component> 086 </pre> 087 * 088 * @author <a href="http://www.dpml.net">Digital Product Meta Library</a> 089 * @version 1.0.1 090 */ 091 public class ComponentDirective extends AbstractDirective implements Comparable, Directive 092 { 093 //-------------------------------------------------------------------------- 094 // static 095 //-------------------------------------------------------------------------- 096 097 /** 098 * Serial version identifier. 099 */ 100 static final long serialVersionUID = 1L; 101 102 private static final CategoriesDirective EMPTY_CATEGORIES = 103 new CategoriesDirective(); 104 105 //-------------------------------------------------------------------------- 106 // state 107 //-------------------------------------------------------------------------- 108 109 /** 110 * The collection policy override. 111 */ 112 private final CollectionPolicy m_collection; 113 114 /** 115 * The component lifestyle policy. 116 */ 117 private final LifestylePolicy m_lifestyle; 118 119 /** 120 * The component classname. 121 */ 122 private final String m_classname; 123 124 /** 125 * The components context directive. 126 */ 127 private final ContextDirective m_context; 128 129 /** 130 * The name of the component profile. This is an 131 * abstract name used during assembly. 132 */ 133 private final String m_name; 134 135 /** 136 * The activation policy. 137 */ 138 private final ActivationPolicy m_activation; 139 140 /** 141 * Logging category directives. 142 */ 143 private final CategoriesDirective m_categories; 144 145 /** 146 * Base directive uri. 147 */ 148 private final URI m_uri; 149 150 /** 151 * Base directive. 152 */ 153 private final DefaultComposition m_base; 154 155 /** 156 * Internal parts. 157 */ 158 private final PartReference[] m_parts; 159 160 //-------------------------------------------------------------------------- 161 // constructors 162 //-------------------------------------------------------------------------- 163 164 /** 165 * Creation of a new profile. 166 * 167 * @param name the name to assign to the component deployment scenario 168 * @param classname the classname of the component type 169 * @exception IOException if an IO exception occurs 170 */ 171 public ComponentDirective( final String name, final String classname ) throws IOException 172 { 173 this( 174 name, 175 ActivationPolicy.SYSTEM, 176 CollectionPolicy.SYSTEM, 177 LifestylePolicy.SYSTEM, 178 classname, 179 null, null, null, null ); 180 } 181 182 /** 183 * Creation of a new deployment profile. 184 * @param name the name to assign to the created profile 185 * @param activation the component activation policy 186 * @param collection the component garbage collection policy 187 * @param lifestyle the component lifestyle policy 188 * @param classname the component classname 189 * @param categories logging categories 190 * @param context context directive 191 * @param parts the component internal parts 192 * @param uri URI of the component super-definition 193 * @exception IOException if an IO exception occurs 194 */ 195 public ComponentDirective( 196 final String name, 197 final ActivationPolicy activation, 198 final CollectionPolicy collection, 199 final LifestylePolicy lifestyle, 200 final String classname, 201 final CategoriesDirective categories, 202 final ContextDirective context, 203 final PartReference[] parts, 204 final URI uri ) throws IOException 205 { 206 if( null == activation ) 207 { 208 if( null != uri ) 209 { 210 m_activation = null; 211 } 212 else 213 { 214 m_activation = ActivationPolicy.SYSTEM; 215 } 216 } 217 else 218 { 219 m_activation = activation; 220 } 221 222 if( null == categories ) 223 { 224 if( null != uri ) 225 { 226 m_categories = null; 227 } 228 else 229 { 230 m_categories = EMPTY_CATEGORIES; 231 } 232 } 233 else 234 { 235 m_categories = categories; 236 } 237 238 if( null == classname ) 239 { 240 if( null != uri ) 241 { 242 m_classname = null; 243 } 244 else 245 { 246 m_classname = Object.class.getName(); 247 } 248 } 249 else 250 { 251 m_classname = classname; 252 } 253 254 if( null == context ) 255 { 256 if( null != uri ) 257 { 258 m_context = null; 259 } 260 else 261 { 262 m_context = new ContextDirective( new PartReference[0] ); 263 } 264 } 265 else 266 { 267 m_context = context; 268 } 269 270 if( null == lifestyle ) 271 { 272 if( null != uri ) 273 { 274 m_lifestyle = null; 275 } 276 else 277 { 278 m_lifestyle = LifestylePolicy.SYSTEM; 279 } 280 } 281 else 282 { 283 m_lifestyle = lifestyle; 284 } 285 286 if( null == collection ) 287 { 288 if( null != uri ) 289 { 290 m_collection = null; 291 } 292 else 293 { 294 m_collection = CollectionPolicy.SYSTEM; 295 } 296 } 297 else 298 { 299 m_collection = collection; 300 } 301 302 if( null == parts ) 303 { 304 m_parts = new PartReference[0]; 305 } 306 else 307 { 308 m_parts = parts; 309 } 310 311 m_uri = uri; 312 if( null != uri ) 313 { 314 Part part = Part.load( uri, false ); 315 if( part instanceof DefaultComposition ) 316 { 317 m_base = (DefaultComposition) part; 318 } 319 else 320 { 321 final String error = 322 "Base part class is not recognized." 323 + "\nClass: " + part.getClass().getName(); 324 throw new IllegalStateException( error ); 325 } 326 } 327 else 328 { 329 m_base = null; 330 } 331 332 if( null == name ) 333 { 334 if( null != uri ) 335 { 336 m_name = null; 337 } 338 else 339 { 340 m_name = toName( m_classname ); 341 } 342 } 343 else 344 { 345 validateName( name ); 346 m_name = name; 347 } 348 } 349 350 //-------------------------------------------------------------------------- 351 // implementation 352 //-------------------------------------------------------------------------- 353 354 /** 355 * Returns the parts declared by this component type. 356 * 357 * @return the part descriptors 358 */ 359 public PartReference[] getPartReferences() 360 { 361 return m_parts; 362 } 363 364 /** 365 * Retrieve an identified directive. 366 * 367 * @param key the directive key 368 * @return the directive or null if the directive key is unknown 369 */ 370 public Directive getDirective( final String key ) 371 { 372 for ( int i = 0; i < m_parts.length; i++ ) 373 { 374 PartReference reference = m_parts[i]; 375 if( reference.getKey().equals( key ) ) 376 { 377 return reference.getDirective(); 378 } 379 } 380 return null; 381 } 382 383 /** 384 * Return the profile name. 385 * 386 * @return the name of the component. 387 */ 388 public String getName() 389 { 390 return m_name; 391 } 392 393 /** 394 * Return the logging categories for the profile. 395 * 396 * @return the categories 397 */ 398 public CategoriesDirective getCategoriesDirective() 399 { 400 return m_categories; 401 } 402 403 /** 404 * Get the activation policy for the profile. 405 * 406 * @return the declared activation policy 407 * @see ActivationPolicy#SYSTEM 408 * @see ActivationPolicy#STARTUP 409 * @see ActivationPolicy#DEMAND 410 */ 411 public ActivationPolicy getActivationPolicy() 412 { 413 return m_activation; 414 } 415 416 /** 417 * Return the component type classname. 418 * 419 * @return classname of the component type 420 */ 421 public String getClassname() 422 { 423 return m_classname; 424 } 425 426 /** 427 * Return the component lifestyle policy. 428 * 429 * @return the lifestyle policy value 430 */ 431 public LifestylePolicy getLifestylePolicy() 432 { 433 return m_lifestyle; 434 } 435 436 /** 437 * Return the component collection policy. If null, the component 438 * type collection policy will apply. 439 * 440 * @return a HARD, WEAK, SOFT or SYSTEM 441 */ 442 public CollectionPolicy getCollectionPolicy() 443 { 444 return m_collection; 445 } 446 447 /** 448 * Return the context directive for the profile. 449 * 450 * @return the ContextDirective for the profile. 451 */ 452 public ContextDirective getContextDirective() 453 { 454 return m_context; 455 } 456 457 /** 458 * Return the base directive uri. 459 * 460 * @return the uri of the base directive 461 */ 462 public URI getBaseURI() 463 { 464 return m_uri; 465 } 466 467 /** 468 * Return the base directive. 469 * 470 * @return the base directive 471 */ 472 public ComponentDirective getBaseDirective() 473 { 474 if( null == m_base ) 475 { 476 return null; 477 } 478 else 479 { 480 return m_base.getComponentDirective(); 481 } 482 } 483 484 /** 485 * Return the base part. 486 * 487 * @return the base part 488 */ 489 public DefaultComposition getBasePart() 490 { 491 return m_base; 492 } 493 494 /** 495 * Returns a string representation of the profile. 496 * @return a string representation 497 */ 498 public String toString() 499 { 500 if( null != m_name ) 501 { 502 return "[" + getName() + "]"; 503 } 504 else 505 { 506 return "[unknown]"; 507 } 508 } 509 510 /** 511 * Compare this object with the supplied object. 512 * @param object the object to compare with 513 * @return the result 514 */ 515 public int compareTo( Object object ) 516 { 517 String name = this.toString(); 518 String other = object.toString(); 519 return name.compareTo( other ); 520 } 521 522 /** 523 * Test if the supplied object is equal to this object. 524 * @param other the object to compare with this instance 525 * @return TRUE if the supplied object is equal to this object 526 */ 527 public boolean equals( Object other ) 528 { 529 if( !super.equals( other ) ) 530 { 531 return false; 532 } 533 if( !( other instanceof ComponentDirective ) ) 534 { 535 return false; 536 } 537 ComponentDirective profile = (ComponentDirective) other; 538 if( !equals( m_name, profile.getName() ) ) 539 { 540 return false; 541 } 542 else if( !equals( m_activation, profile.getActivationPolicy() ) ) 543 { 544 return false; 545 } 546 else if( !equals( m_categories, profile.getCategoriesDirective() ) ) 547 { 548 return false; 549 } 550 else if( !equals( m_classname, profile.getClassname() ) ) 551 { 552 return false; 553 } 554 else if( !equals( m_context, profile.getContextDirective() ) ) 555 { 556 return false; 557 } 558 else if( !equals( m_collection, profile.getCollectionPolicy() ) ) 559 { 560 return false; 561 } 562 else if( !equals( m_lifestyle, profile.getLifestylePolicy() ) ) 563 { 564 return false; 565 } 566 else if( !Arrays.equals( m_parts, profile.getPartReferences() ) ) 567 { 568 return false; 569 } 570 else 571 { 572 return equals( m_uri, profile.getBaseURI() ); 573 } 574 } 575 576 /** 577 * Return the hashcode for the instance. 578 * @return the instance hashcode 579 */ 580 public int hashCode() 581 { 582 int hash = super.hashCode(); 583 hash ^= hashValue( m_name ); 584 hash ^= hashValue( m_activation ); 585 hash ^= hashValue( m_categories ); 586 hash ^= hashValue( m_classname ); 587 hash ^= hashValue( m_context ); 588 hash ^= hashValue( m_collection ); 589 hash ^= hashValue( m_lifestyle ); 590 hash ^= hashValue( m_uri ); 591 hash ^= hashArray( m_parts ); 592 return hash; 593 } 594 595 /** 596 * Internal utility to get the name of the class without the package name. Used 597 * when constructing a default component name. 598 * @param classname the fully qualified classname 599 * @return the short class name without the package name 600 */ 601 private String toName( String classname ) 602 { 603 int i = classname.lastIndexOf( "." ); 604 if( i == -1 ) 605 { 606 return classname.toLowerCase(); 607 } 608 else 609 { 610 return classname.substring( i + 1, classname.length() ).toLowerCase(); 611 } 612 } 613 614 private void validateName( final String name ) 615 { 616 if( name.indexOf( " " ) > 0 || name.indexOf( "." ) > 0 || name.indexOf( "," ) > 0 617 || name.indexOf( "/" ) > 0 ) 618 { 619 final String error = 620 "Directive name [" 621 + name 622 + "] contains an illegal character (' ', ',', '/', or '.')"; 623 throw new IllegalArgumentException( error ); 624 } 625 else if( name.length() == 0 ) 626 { 627 final String error = 628 "Directive name [] is insufficient."; 629 throw new IllegalArgumentException( error ); 630 } 631 } 632 }